﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace arthuraafn_xna_3d.Base
{

    class Terrain:DrawableGameComponent 
    {
        BasicEffect basicEffect;
        BasicCamera camera;
        VertexPositionNormalTexture[] m_vertices;
        int[] m_indices;
        Texture2D texture;

        int terrainWidth;
        int terrainHeight;

        float[,] heightData;

        public Terrain(Game game,BasicCamera camera):base(game)
        {
            //camera
            this.camera = camera;

            //Criar novo efeito básico e propriedades:
            basicEffect = new BasicEffect(game.GraphicsDevice);

            basicEffect.EnableDefaultLighting();
            //basicEffect.FogEnabled = true;
            //basicEffect.FogStart = 300f;
            //basicEffect.FogEnd = 1000f;
            //basicEffect.FogColor = Color.Black.ToVector3();

            //ativando as cores
            //basicEffect.VertexColorEnabled = false ;


            texture = Game.Content.Load<Texture2D>("Textures/grass");

            basicEffect.TextureEnabled = true;
            basicEffect.Texture = texture;



        }


        public void CreateTerrainRandon(int worldWidthX, int worldWidthZ, int numCellsX, int numCellsZ)
        {
            int numVerticesWide = numCellsX + 1;
            int numVerticesHigh = numCellsZ + 1;
            int totalVertices = numVerticesHigh * numVerticesWide;

            int numTrianglesWide = numCellsX * 2;
            int numTrianglesHeigh = numCellsZ * 2;
            int totalTriangles = numTrianglesWide * numTrianglesHeigh;

            m_indices = new int[totalTriangles * 3];
            m_vertices = new VertexPositionNormalTexture[totalVertices + 1];

            int startXposition = -(worldWidthX / 2);
            int startZposition = -(worldWidthZ / 2);

            int cellXsize = worldWidthX / numCellsX;
            int cellZsize = worldWidthZ / numCellsZ;

            int worldZposition;
            int worldXPosition;

            Random rand;
            rand = new Random();

            worldZposition = startZposition;

            // Fill in the vertices
            int count = 0;
            int worldZPosition = startZposition; 
            for (int y = 0; y < numVerticesHigh; y++)
            {
                worldXPosition = startXposition;
                for (int x = 0; x < numVerticesWide; x++)
                {

                    m_vertices[count].Position = new Vector3(worldXPosition, rand.Next(2), worldZPosition);
                    m_vertices[count].Normal = Vector3.Up;
                    m_vertices[count].TextureCoordinate.X = (float)x / (numVerticesWide - 1);
                    m_vertices[count].TextureCoordinate.Y = (float)y / (numVerticesHigh - 1);

                    count++;

                    // Advance in x
                    worldXPosition += cellXsize;
                }
                // Advance in z
                worldZPosition += cellZsize;
            }

            CalcIndices(numCellsZ, numCellsX, numVerticesWide); 

            CalcNormals();
        }


        public void CreateTerrainTexture(String TextureName)
        {
            Texture2D heightMap = Game.Content.Load<Texture2D>(TextureName);

            LoadHeightData(heightMap);

            int worldWidthX = terrainWidth;
            int worldWidthZ = terrainHeight;

            int cellXsize = 5; //worldWidthX / numCellsX;
            int cellZsize = 5; //worldWidthZ / numCellsZ;

            int numCellsX = worldWidthX / cellXsize;
            int numCellsZ = worldWidthZ / cellZsize;

            int numVerticesWide = numCellsX + 1;
            int numVerticesHigh = numCellsZ + 1;
            int totalVertices = numVerticesHigh * numVerticesWide;

            int numTrianglesWide = numCellsX * 2;
            int numTrianglesHeigh = numCellsZ * 2;
            int totalTriangles = numTrianglesWide * numTrianglesHeigh;

            m_indices = new int[totalTriangles * 3];
            m_vertices = new VertexPositionNormalTexture[totalVertices + 1];

            int startXposition = -(worldWidthX / 2);
            int startZposition = -(worldWidthZ / 2);


            int worldZposition;
            int worldXPosition;

            worldZposition = startZposition;

            // Fill in the vertices
            int count = 0;
            int mapX,mapZ = 0;
            int worldZPosition = startZposition;
            for (int y = 0; y < numVerticesHigh; y++)
            {
                mapZ = worldZPosition - startXposition;
                if (mapZ > terrainHeight - 1)
                    mapZ = terrainHeight - 1;

                worldXPosition = startXposition;

                for (int x = 0; x < numVerticesWide; x++)
                {
                    mapX = worldXPosition - startXposition;
                    if (mapX > terrainWidth - 1)
                        mapX = terrainWidth - 1;
                    m_vertices[count].Position = new Vector3(worldXPosition, heightData[mapX,mapZ], worldZPosition);
                    m_vertices[count].Normal = Vector3.Up;
                    m_vertices[count].TextureCoordinate.X = (float)x*8 / numVerticesWide;
                    m_vertices[count].TextureCoordinate.Y = (float)y*8 / numVerticesHigh;

                    count++;

                    // Advance in x
                    worldXPosition += cellXsize;
                }
                // Advance in z
                worldZPosition += cellZsize;
            }

            CalcIndices(numCellsZ, numCellsX, numVerticesWide); 

            CalcNormals();
        }


        private void CalcIndices(int numCellsZ, int numCellsX, int numVerticesWide)
        {
            int index = 0;
            int startVertex = 0;
            for (int cellZ = 0; cellZ < numCellsZ; cellZ++)
            {
                for (int cellX = 0; cellX < numCellsX; cellX++)
                {
                    m_indices[index] = startVertex + 0;
                    m_indices[index + 1] = startVertex + 1;
                    m_indices[index + 2] = startVertex + numVerticesWide;

                    index += 3;

                    m_indices[index] = startVertex + 1;
                    m_indices[index + 1] = startVertex + numVerticesWide + 1;
                    m_indices[index + 2] = startVertex + numVerticesWide;

                    index += 3;

                    startVertex++;
                }
                startVertex++;
            }
        }

        private void MapterrainHeightComplete()
        {
            for (int x = terrainWidth ; x < terrainWidth; x++)
                heightData[x, terrainHeight] = heightData[x, terrainHeight - 1];
            for (int y = 0; y < terrainHeight; y++)
                heightData[terrainWidth, y] = heightData[terrainWidth - 1, y];

        }

        private void CalcNormals()
        {
             for (int i = 0; i < m_vertices.Length; i++)
                    m_vertices[i].Normal = new Vector3(0, 0, 0);
 
            for (int i = 0; i < m_indices.Length / 3; i++)
            {
                int index1 = m_indices[i * 3];
                int index2 = m_indices[i * 3 + 1];
                int index3 = m_indices[i * 3 + 2];
 
                Vector3 side1 = m_vertices[index1].Position - m_vertices[index3].Position;
                Vector3 side2 = m_vertices[index1].Position - m_vertices[index2].Position;
                Vector3 normal = Vector3.Cross(side1, side2);
 
                m_vertices[index1].Normal += normal;
                m_vertices[index2].Normal += normal;
                m_vertices[index3].Normal += normal;                
            }

            for (int i = 0; i < m_vertices.Length; i++)
                m_vertices[i].Normal.Normalize();
        }

        public override void Draw(GameTime gameTime)
        {

            basicEffect.View = camera.ViewMatrix;
            basicEffect.Projection = camera.ProjectionMatrix;

            basicEffect.World = Matrix.Identity;

            foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
            {
               pass.Apply();

               //desenhando as primitivas
               Game.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, m_vertices,
                   0, m_vertices.Length, m_indices,  0, m_indices.Length / 3);


            }

            //passando o efeito básico
            //basicEffect.CurrentTechnique.Passes[0].Apply();

            //desenhando as primitivas
            //Game.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, m_vertices,
            //    0, m_vertices.Length, m_indices, 0, m_indices.Length / 3);
        }

        private void LoadHeightData(Texture2D heightMap)
        {
            terrainWidth = heightMap.Width;
            terrainHeight = heightMap.Height;
 
            Color[] heightMapColors = new Color[terrainWidth * terrainHeight];
            heightMap.GetData(heightMapColors);
 
            heightData = new float[terrainWidth, terrainHeight];
            for (int x = 0; x < terrainWidth; x++)
                for (int y = 0; y < terrainHeight; y++)
                    heightData[x, y] = heightMapColors[x + y * terrainWidth].R / 5.0f;

        }        
    }
}
